import type { ReactNode } from 'react'
import React, {
  forwardRef,
  memo,
  useEffect,
  useImperativeHandle,
  useState
} from 'react'
import {
  Cascader,
  Col,
  DatePicker,
  Form,
  FormInstance,
  Input,
  InputNumber,
  Radio,
  Row,
  Select
} from 'antd'
import { FormType, IMyFormProps } from '@/base-ui/base-form/type'
import { ActionDesc, PagesKey } from '@/global/constant'

interface IProps {
  children?: ReactNode
  config: IMyFormProps
  // 跟page-search有关，控制表单项渲染多少个
  isSearch?: boolean
  isExpand?: boolean
}

export interface IBaseFormComponentRef {
  getFormInstance: () => FormInstance
  updateFormView: (values: any) => void
}

const BaseForm = (props: IProps, ref: any) => {
  const { isSearch, isExpand } = props
  const { layout, formConfig, itemsConfig, desc, pageKey } = props.config
  const [myForm] = Form.useForm()

  // 向外暴露方法
  useImperativeHandle(ref, () => ({
    getFormInstance() {
      return myForm
    },
    updateFormView(values: any) {
      setChangedData(values)
    }
  }))

  // 根据配置里面的customkey动态渲染父组件传来的children
  function findCustomChild(key?: string) {
    if (Array.isArray(props.children)) {
      return props.children.find((child) => child.key === key)
    } else return props.children
  }

  /**
   * 有些页面的表单比较复杂，比如点击一个表单项，隐藏另外一个的
   * 这些我们不可能做到统一处理，所以这时候通过des属性来做判断处理
   * desc是唯一标识，开始是当前表单是用户页的新增或者角色的编辑等等。
   */
  // rowData保存的是每次变化的值
  const [changedData, setChangedData] = useState<any>(formConfig.initialValues)
  const handleFormValuesChange = (changedValues: any, allValues: any) => {
    // if (desc !== FormDesc.SecondAuth) return
    setChangedData(allValues)
    console.log('变化的表单项的值：', changedValues)
  }

  // 跟编辑数据回流--表单默认值相关
  useEffect(() => {
    /**
     * 当监听到父给子传的表单的配置项发生了变化，就会先挂载dom节点，然后执行useEffect里面的回调
     * 如果当前的表单是编辑的话，那么我们就不应该有默认值formConfig.initialValues，因为当前有一个问题：
     * 如果我们有默认值，而且也要编辑回显数据，父组件那里点击某一行拿到数据后通过手动调用form实例上的setFields()
     * 手动给表单加上要回显的值；但是当来到这个回调的时候会先赋予回显的值，然后再调用myForm.setFieldsValue(formConfig.initialValues)
     * 就会把编辑回显的值给覆盖。
     * 总结：formConfig.initialValues会在回显后再设置给表单，所以默认值跟编辑不能同时存在。
     * 所以做了下面的判断：只有在编辑的表单里面去获取原来的值赋值给
     */
    if (desc === ActionDesc.Edit) {
      const values = myForm.getFieldsValue()
      setChangedData(values)
    } else {
      myForm.setFieldsValue(formConfig.initialValues)
      setChangedData(formConfig.initialValues)
    }
  }, [props])

  function handleItemsConnection(prop: string) {
    // 如果 rawData 不存在或者 desc 不属于 [ActionDesc.SecondAuth, ActionDesc.UserBanned, ActionDesc.Edit, ActionDesc.Add] 中的任何一个，则返回 true
    if (
      !changedData ||
      ![
        ActionDesc.SecondAuth,
        ActionDesc.UserBanned,
        ActionDesc.Edit,
        ActionDesc.Add
      ].includes(desc)
    ) {
      return true
    }

    if (desc === ActionDesc.SecondAuth) {
      // 如果 desc 为 ActionDesc.SecondAuth，则根据 rawData.type 进行判断
      if (changedData.type === 0) {
        return !(prop === 'code' || prop === 'email' || prop === 'userPhone')
      } else if (changedData.type === 1) {
        return !(prop === 'password' || prop === 'email')
      } else if (changedData.type === 2) {
        return !(prop === 'password' || prop === 'userPhone')
      }
    }

    // 用户页--用户禁止表单的限制
    if (desc === ActionDesc.UserBanned) {
      // 如果 desc 为 ActionDesc.UserBanned，则判断是否为 type 为 1 并且属性为 'time'
      return !(changedData.type === 1 && prop === 'time')
    }

    // 菜单页--编辑和新增限制
    if (
      (desc === ActionDesc.Edit || desc === ActionDesc.Add) &&
      pageKey === PagesKey.MenuList
    ) {
      // 如果 desc 为 ActionDesc.Edit 或 ActionDesc.Add，并且 pageKey 为 PagesKey.MenuList，则根据 rawData.type 进行判断
      if (changedData.type === 0) {
        return !(prop === 'url')
      } else if (changedData.type === 2) {
        return !(
          prop === 'elIcon' ||
          prop === 'url' ||
          prop === 'path' ||
          prop === 'name'
        )
      }
    }

    // 如果以上条件都不满足，则返回 true
    return true
  }

  return (
    <div>
      <Form
        {...formConfig}
        form={myForm}
        onValuesChange={handleFormValuesChange}
      >
        <Row gutter={24}>
          {itemsConfig.map((item, index) => {
            const {
              type,
              prop,
              select,
              radio,
              input,
              inputNumber,
              password,
              textArea,
              search,
              date_picker,
              range_picker,
              cascader,
              ...newItemConfig
            } = item
            // 只有pageSearch组件才有机会跳出循环只渲染部分表单项
            if (isSearch && !isExpand && index >= 3) return
            return (
              <Col key={prop} span={layout?.span || 24}>
                {handleItemsConnection(item.prop) && (
                  <Form.Item {...newItemConfig}>
                    {type === FormType.Input && <Input {...input} />}
                    {type === FormType.Password && (
                      <Input.Password {...password} />
                    )}
                    {type === FormType.TextArea && (
                      <Input.TextArea {...textArea} />
                    )}
                    {type === FormType.InputNumber && (
                      <InputNumber {...inputNumber} />
                    )}
                    {type === FormType.Search && <Input.Search {...search} />}
                    {type === FormType.Select && (
                      <Select {...select?.baseProps}>
                        {select?.options?.map((item) => {
                          return (
                            <Select.Option key={item.value} value={item.value}>
                              {item.content || item.label}
                            </Select.Option>
                          )
                        })}
                      </Select>
                    )}
                    {type === FormType.Radio && (
                      <Radio.Group {...radio?.baseProps}>
                        {radio?.options?.map((item) => {
                          return (
                            <Radio key={item.value} value={item.value}>
                              {item.content}
                            </Radio>
                          )
                        })}
                      </Radio.Group>
                    )}
                    {type === FormType.DatePicker && (
                      <DatePicker {...date_picker} />
                    )}
                    {type === FormType.RangePicker && (
                      <DatePicker.RangePicker {...range_picker} />
                    )}
                    {type === FormType.Cascader && (
                      <Cascader
                        {...cascader?.baseProps}
                        options={cascader?.options}
                      />
                    )}
                    {type === FormType.Custom &&
                      findCustomChild(item.customkey)}
                  </Form.Item>
                )}
              </Col>
            )
          })}
        </Row>
      </Form>
    </div>
  )
}

export default memo(forwardRef(BaseForm))
